package com.stone.serverless.sdk.utils;

import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.SerializeUtil;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * @Author: huyi
 * @Date: 2021/4/20 14:34
 */
@Getter
@Setter
public class SerializedLambda implements Serializable {

    private static final long serialVersionUID = 8025925345765570181L;

    private static final Integer TWO = 2;
    private Class<?> capturingClass;
    private String functionalInterfaceClass;
    private String functionalInterfaceMethodName;
    private String functionalInterfaceMethodSignature;
    private String implClass;
    private String implMethodName;
    private String implMethodSignature;
    private int implMethodKind;
    private String instantiatedMethodType;
    private Object[] capturedArgs;

    /**
     * 通过反序列化转换 lambda 表达式，该方法只能序列化 lambda 表达式，不能序列化接口实现或者正常非 lambda 写法的对象
     *
     * @param lambda lambda对象
     * @return 返回解析后的 SerializedLambda
     */
    @SneakyThrows
    public static SerializedLambda resolve(Object lambda) {
        //if (!lambda.getClass().isSynthetic()) {
        //    throw new RuntimeException("该方法仅能传入 lambda 表达式产生的合成类");
        //}
        try (ObjectInputStream objIn = new ObjectInputStream(new ByteArrayInputStream(SerializeUtil.serialize(lambda))) {
            @Override
            protected Class<?> resolveClass(ObjectStreamClass objectStreamClass) throws IOException, ClassNotFoundException {
                Class<?> clazz;
                try {
                    clazz = ClassUtil.loadClass(objectStreamClass.getName());
                } catch (UtilException ex) {
                    System.out.println("加载类报异常了");
                    clazz = super.resolveClass(objectStreamClass);
                }
                return clazz == java.lang.invoke.SerializedLambda.class ? SerializedLambda.class : clazz;
            }
        }) {
            return (SerializedLambda) objIn.readObject();
        } catch (ClassNotFoundException | IOException e) {
            System.out.println("This is impossible to happen");
        }
        return null;
    }

    /**
     * 获取接口 class
     *
     * @return 返回 class 名称
     */
    public String getFunctionalInterfaceClassName() {
        return normalizedName(functionalInterfaceClass);
    }

    /**
     * 获取 class 的名称
     *
     * @return 类名
     */
    public String getImplClassName() {
        return normalizedName(implClass);
    }


    /**
     * 正常化类名称，将类名称中的 / 替换为 .
     *
     * @param name 名称
     * @return 正常的类名
     */
    private String normalizedName(String name) {
        return name.replace('/', '.');
    }

    /**
     * @return 获取实例化方法的类型
     */
    public Class<?> getBaseClass() {
        String instantiatedTypeName = normalizedName(this.implClass);
        return ClassUtil.loadClass(instantiatedTypeName);
    }

    public Method getInvokeMethod() {
        Class<?> baseClass = this.getBaseClass();
        Method[] methods = ReflectUtil.getMethods(baseClass);

        return Arrays.stream(methods).filter(v -> {
            String name = v.getName();

            Class<?> tReturnType = v.getReturnType();

            //如果方法名称匹配
            if (CharSequenceUtil.equals(name, this.implMethodName)) {
                Class<?> returnType = getReturnType();
                return returnType.equals(tReturnType);
            }
            return false;

        }).findFirst().orElse(null);

    }

    /**
     * @return 获取实例化方法的类型
     */
    public Class<?> getReturnType() {
        int i = implMethodSignature.indexOf(")") + TWO;
        if (i >= implMethodSignature.length()) {
            return void.class;
        }
        String instantiatedTypeName = normalizedName(implMethodSignature.substring(implMethodSignature.indexOf(")") + TWO, implMethodSignature.indexOf(';')));
        return ClassUtil.loadClass(instantiatedTypeName);
    }

    /**
     * @return 字符串形式
     */
    @Override
    public String toString() {
        String interfaceName = getFunctionalInterfaceClassName();
        String implName = getImplClassName();
        return String.format("%s -> %s::%s",
                interfaceName.substring(interfaceName.lastIndexOf('.') + 1),
                implName.substring(implName.lastIndexOf('.') + 1),
                implMethodName);
    }

}
